package com.haiqiu.system.service.impl;

import com.haiqiu.common.mail.MailService;
import com.haiqiu.common.exception.BaseException;
import com.haiqiu.common.page.PageRequest;
import com.haiqiu.common.page.PageResponse;
import com.haiqiu.common.result.Constants;
import com.haiqiu.common.utils.files.UploadUtil;
import com.haiqiu.common.utils.tools.ExcelUtil;
import com.haiqiu.common.utils.tools.StringUtil;
import com.haiqiu.common.utils.web.JwtTokenUtil;
import com.haiqiu.common.utils.web.RedisUtil;
import com.haiqiu.common.utils.web.ServletUtil;
import com.haiqiu.system.entity.*;
import com.haiqiu.system.entity.dto.LoginDto;
import com.haiqiu.system.entity.dto.RegDto;
import com.haiqiu.system.entity.dto.ResetUserDto;
import com.haiqiu.system.entity.query.UserQuery;
import com.haiqiu.system.entity.vo.OnlineVo;
import com.haiqiu.system.entity.vo.UserPwd;
import com.haiqiu.system.entity.vo.SysUserVo;
import com.haiqiu.system.mapper.*;
import com.haiqiu.system.service.SysConfigService;
import com.haiqiu.system.service.SysDeptService;
import com.haiqiu.system.service.SysUserService;
import org.apache.commons.net.util.Base64;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author HaiQiu
 */
@Service
public class SysUserServiceImpl implements SysUserService {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private SysDeptService sysDeptService;

    @Autowired
    private SysDeptMapper sysDeptMapper;

    @Autowired
    private SysPositionMapper sysPositionMapper;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private SysLoginLogMapper sysLogMapper;

    @Autowired
    private MailService mailService;

    @Autowired
    private UploadUtil uploadUtil;

    @Autowired
    private SysConfigService sysConfigService;

    @Value("${oss.folder}")
    private String avatarPath;

    @Resource
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private ServletUtil servletUtil;


    /**
     * 删除
     *
     * @param id 数据ID
     */
    @Override
    public void deleteByPrimaryKey(Long id) {
        if (sysUserMapper.deleteByPrimaryKey(id) == 0) {
            throw new BaseException(Constants.FAIL_DEL);
        }
    }


    /**
     * 账户新增
     *
     * @param userVo 账户实体
     */
    @Override
    public void save(SysUserVo userVo) {
        SysUser sysUser = new SysUser();
        BeanUtils.copyProperties(userVo, sysUser);
        if (StringUtils.isEmpty(sysUser.getPassword())) {
            throw new BaseException("密码不能为空");
        }
        //检验用户信息是否存在重复
        checkUser(userVo);
        Date date = new Date();
        // 设置密码加密
        sysUser.setPassword(passwordEncoder.encode(sysUser.getPassword()));
        sysUser.setCreateTime(date);
        sysUser.setUpdateTime(date);
        sysUser.setDel(Constants.OFF);
        sysUser.setActive(Constants.ON);
        if (StringUtils.isEmpty(sysUser.getSex())) {
            sysUser.setSex(Constants.SEX_SECRET);
        }
        if (sysUserMapper.insert(sysUser) == 0) {
            throw new BaseException(Constants.FAIL_ADD);
        }
        if (StringUtils.isEmpty(userVo.getRoleIds())) {
            throw new BaseException("请选择账户角色");
        }
        //添加用户关联的角色和职位信息
        addRoleByUserId(sysUser.getId(), userVo.getRoleIds());
        addPositionByUserId(sysUser.getId(), userVo.getPositionIds());

    }

    /**
     * 检验用户信息是否存在重复
     *
     * @param userVo 用户实体
     */
    private void checkUser(SysUserVo userVo) {
        List<String> account = new ArrayList<>();
        if (!StringUtils.isEmpty(userVo.getUsername())) {
            account.add(userVo.getUsername());
        }
        if (!StringUtils.isEmpty(userVo.getEmail())) {
            account.add(userVo.getEmail());
        }
        if (!StringUtils.isEmpty(userVo.getPhone())) {
            account.add(userVo.getPhone());
        }
        for (String s : account) {
            SysUser login = sysUserMapper.login(s);
            if (login != null) {
                if (login.getUsername().equals(userVo.getUsername())) {
                    throw new BaseException("用户名已存在");
                } else if (login.getEmail().equals(userVo.getEmail())) {
                    throw new BaseException("邮箱已存在");
                } else if (login.getPhone().equals(userVo.getPhone())) {
                    throw new BaseException("手机已存在");
                }
            }
        }
    }


    /**
     * 查询单条
     *
     * @param id 数据ID
     * @return 账户
     */
    @Override
    public SysUser selectByPrimaryKey(Long id) {
        SysUser sysUser = sysUserMapper.selectByPrimaryKey(id);
        if (sysUser == null) {
            throw new BaseException(Constants.DATA_EXIT);
        }
        return sysUser;
    }


    /**
     * 修改账户
     *
     * @param userVo 账户实体
     */
    @Override
    @Transactional(rollbackFor = BaseException.class)
    public void update(SysUserVo userVo) {
        SysUser user = sysUserMapper.selectByPrimaryKey(userVo.getId());
        if (user == null) {
            throw new BaseException("账户不存在，可能已经被删除了");
        }
        SysUser sysUser = new SysUser();
        BeanUtils.copyProperties(userVo, sysUser);
        sysUser.setUpdateTime(new Date());
        // TODO 修改用户此方法不执行密码修改操作，故密码设置为null
        sysUser.setPassword(null);
        if (sysUserMapper.updateByPrimaryKeySelective(sysUser) == 0) {
            throw new BaseException(Constants.FAIL_UPDATE);
        }
        //关联角色操作
        delRoleByUserId(userVo.getId());
        addRoleByUserId(userVo.getId(), userVo.getRoleIds());
        //关联职位操作
        delPositionByUserId(userVo.getId());
        addPositionByUserId(userVo.getId(), userVo.getPositionIds());
        //清除缓存用户的数据
        delRedisUser(sysUser.getUsername());
    }

    /**
     * 根据用户ID删除对应的角色
     *
     * @param userId 用户ID
     */
    public void delRoleByUserId(Long userId) {
        sysRoleMapper.delRoleByUserId(userId);
    }

    /**
     * 根据用户ID删除对应的职位
     *
     * @param userId 用户ID
     */
    public void delPositionByUserId(Long userId) {
        sysPositionMapper.delPositionByUserId(userId);
    }


    /**
     * 根据账户ID添加对应的角色
     *
     * @param userId  账户ID
     * @param roleIds 角色ID
     */
    public void addRoleByUserId(Long userId, List<Long> roleIds) {
        if (sysRoleMapper.addRoleByUserId(userId, roleIds) == 0) {
            throw new BaseException("添加账户对应角色失败");
        }
    }

    /**
     * 根据账户ID添加对应的职位
     *
     * @param userId      账户ID
     * @param positionIds 职位ID
     */
    public void addPositionByUserId(Long userId, List<Long> positionIds) {
        if (sysPositionMapper.addPositionByUserId(userId, positionIds) == 0) {
            throw new BaseException("添加账户对应职位失败");
        }
    }


    /**
     * 登录
     *
     * @param loginDto 登录实体
     * @return 认证信息（token）
     */
    @Override
    public Map<String, Object> login(LoginDto loginDto) {
        //判断系统配置是否检测验证码
        if (sysConfigService.get(Constants.CONFIG_ID).getCaptcha()) {
            //检测验证码
            checkCaptcha(loginDto.getCaptcha(), loginDto.getKey());
        }
        //登录
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken
                (loginDto.getUsername(), loginDto.getPassword());
        //保存登录日志
        SysLoginLog sysLoginLog = setLoginLog(loginDto);
        // 用户验证
        Authentication authentication = null;
        //error msg捕捉
        String msg = null;
        try {
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
        } catch (Exception e) {
            //此异常抛出在源码DaoAuthenticationProvider类的该方法会去调用UserDetailsServiceImpl下执行
            if (e instanceof InternalAuthenticationServiceException) {
                msg = Constants.USER_NOT_EXIST;
                throw new InternalAuthenticationServiceException(Constants.USER_NOT_EXIST);
            } else if (e instanceof BadCredentialsException) {
                msg = Constants.PASSPWD_ERROR;
                throw new BadCredentialsException(Constants.PASSPWD_ERROR);
            } else if (e instanceof LockedException) {
                msg = Constants.USER_DISABLE;
                throw new LockedException(Constants.USER_DISABLE);
            } else {
                msg = e.getMessage();
                throw new BaseException(e.getMessage());
            }
        } finally {
            if (msg != null) {
                sysLoginLog.setResult(msg);
                sysLoginLog.setResponseJson(ServletUtil.getHttpServletResponse().toString());
                sysLogMapper.insert(sysLoginLog);
            }
        }

        //根据security授权之后的用户设置redis缓存信息
        SysUser sysUser = setRedisUserInfo(authentication);

        //返回token令牌信息
        String token = jwtTokenUtil.generateToken(sysUser.getId(), sysUser.getUsername());
        Map<String, Object> map = new HashMap<>(2);
        map.put("token", token);

        //判断是否为单端登录，是则 保持redis唯一key，否则多个key（设置缓存token）
        setRedisToken(loginDto, token);

        sysLoginLog.setResult("登录成功");
        sysLoginLog.setResponseJson(map.toString());
        sysLogMapper.insert(sysLoginLog);
        return map;
    }

    /**
     * 保存基本登录日志
     *
     * @param loginDto 登录日志实体
     * @return 基本登录日志
     */
    private SysLoginLog setLoginLog(LoginDto loginDto) {
        SysLoginLog sysLoginLog = new SysLoginLog();
        sysLoginLog.setBrowser(ServletUtil.getBrowser(ServletUtil.getHttpServletRequest()));
        sysLoginLog.setOs(ServletUtil.getOs(ServletUtil.getHttpServletRequest()));
        sysLoginLog.setRequestIp(ServletUtil.getIpAddr(ServletUtil.getHttpServletRequest()));
        sysLoginLog.setRequestAddress(ServletUtil.getAddress(sysLoginLog.getRequestIp()));
        sysLoginLog.setUsername(loginDto.getUsername());
        sysLoginLog.setRequestParam(loginDto.toString());
        sysLoginLog.setRestUrl(ServletUtil.getHttpServletRequest().getRequestURL().toString());
        sysLoginLog.setCreateTime(new Date());
        sysLoginLog.setUpdateTime(new Date());
        return sysLoginLog;
    }

    /**
     * 根据security授权之后的用户设置redis缓存信息
     *
     * @param authentication 授权对象
     * @return 用户信息
     */
    private SysUser setRedisUserInfo(Authentication authentication) {
        UserDetails principal = (UserDetails) authentication.getPrincipal();
        SysUser sysUser = null;
        if (redisUtil.hasKey(Constants.REDIS_USER_INFO + principal.getUsername())) {
            sysUser = (SysUser) redisUtil.get(Constants.REDIS_USER_INFO + principal.getUsername());
        } else {
            sysUser = sysUserMapper.selectByUsername(principal.getUsername());
        }
        return sysUser;
    }

    /**
     * 根据单端和多端登录缓存用户token信息
     *
     * @param loginDto 用户信息
     * @param token    token令牌
     */
    private void setRedisToken(LoginDto loginDto, String token) {
        if (!isManyLogin()) {
            //缓存用户token，有效期一小时
            redisUtil.set(Constants.USER_TOKEN + loginDto.getUsername(),
                    token,
                    sysConfigService.get(Constants.CONFIG_ID).getTokenTime());
        } else {
            //缓存用户token，有效期一小时
            redisUtil.set(Constants.USER_TOKEN + loginDto.getUsername() + "_" + token,
                    token,
                    sysConfigService.get(Constants.CONFIG_ID).getTokenTime());
        }
    }

    /**
     * 判断是否为单端登录
     *
     * @return true是，false否
     */
    private Boolean isManyLogin() {
        SysConfig sysConfig = sysConfigService.get(Constants.CONFIG_ID);
        if (sysConfig.getManyLogin() == null) {
            sysConfig.setManyLogin(Constants.OFF);
        }
        return sysConfig.getManyLogin();
    }

    /**
     * 验证码检测
     *
     * @param captcha 验证码
     * @param key     验证码key
     */
    private void checkCaptcha(String captcha, String key) {
        if (StringUtils.isEmpty(captcha)) {
            throw new BaseException("验证码为空");
        }
        if (!redisUtil.hasKey(Constants.CAPTCHA_KEY + key)) {
            throw new BaseException("验证码已过期");
        }
        if (!captcha.equals(redisUtil.get(Constants.CAPTCHA_KEY + key))) {
            throw new BaseException("验证码不正确");
        }
    }


    /**
     * 根据用户名获取用户信息
     *
     * @return 用户信息
     */
    @Override
    public SysUserVo info(String token) {
        //获取用户名
        String username = null;
        if (StringUtils.isEmpty(token)) {
            HttpServletRequest request = ServletUtil.getHttpServletRequest();
            username = jwtTokenUtil.getUsername(request);
        } else {
            username = jwtTokenUtil.getUsername(token);
        }
        //获取用户信息
        SysUserVo userVo = null;
        //如果存在redis，则redis获取
        if (redisUtil.hasKey(Constants.REDIS_USER_INFO + username)) {
            userVo = (SysUserVo) redisUtil.get(Constants.REDIS_USER_INFO + username);
            //否则数据库获取
        } else {
            SysUser sysUser = sysUserMapper.userInfoByUsername(username);
            BeanUtils.copyProperties(sysUser, userVo);
            List<SysRole> sysRoles = sysRoleMapper.selectRoleByUserId(userVo.getId());
            List<String> rolesByName = sysRoles.stream().map(SysRole::getName).collect(Collectors.toList());
            userVo.setRoles(rolesByName);
            userVo.setDept(sysDeptService.get(userVo.getDeptId()));
            //存储到redis
            redisUtil.set(Constants.REDIS_USER_INFO + username, userVo);
        }
        return userVo;
    }


    /**
     * 模糊分页
     *
     * @param request 请求实体
     * @return 分页数据
     */
    @Override
    public PageResponse<SysUserVo> list(PageRequest<UserQuery> request) {
        //过滤空字符数据
        UserQuery userQuery = StringUtil.checkObjFieldIsNull(request.getParams());
        //如果部门ID不是null
        if (userQuery.getDeptId() != null) {
            // 查询部门本级和所有子级
            List<SysDept> sysDepts = sysDeptService.selectChildrenByPrimaryKey(userQuery.getDeptId());
            Set<Long> deptIds = sysDepts.stream().map(SysDept::getId).collect(Collectors.toSet());
            userQuery.setDeptIds(deptIds);
        }
        long count = sysUserMapper.count(userQuery);
        PageResponse<SysUserVo> response = new PageResponse<>();
        response.setPageSize(request.getPageSize());
        response.setPageIndex(request.getPageIndex());
        response.setTotal(count);
        if (count > 0) {
            List<SysUserVo> list = sysUserMapper.list(userQuery, request.getOffset(), request.getPageSize());
            List<SysUserVo> userVos = assembleDept(list);
            response.setData(userVos);
        }
        return response;
    }

    /**
     * 装配用户部门信息
     *
     * @param list 用户集合
     */
    private List<SysUserVo> assembleDept(List<SysUserVo> list) {
        for (SysUserVo userVo : list) {
            if (userVo.getDeptId() != null) {
                SysDept dept = sysDeptService.get(userVo.getDeptId());
                SysDept sysDept = new SysDept();
                sysDept.setDeptName(dept.getDeptName());
                sysDept.setId(dept.getId());
                userVo.setDept(sysDept);
            }
        }
        return list;
    }


    /**
     * 批量删除用户
     *
     * @param ids 数据ID集合
     * @return 成功条数
     */
    @Override
    public int delete(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            throw new BaseException(Constants.ID_EXITS);
        }
        for (Long id : ids) {
            SysUserVo userVo = sysUserMapper.getById(id);
            if (userVo.getUsername().equals(servletUtil.getUserName())) {
                throw new BaseException("删除的账户中包含自己，删除失败！");
            }
            //删除检测并删除redis账户
            if (userVo != null) {
                delRedisUser(userVo.getUsername());
            }
            // 删除账户绑定的角色
            this.delRoleByUserId(id);
        }
        int i = sysUserMapper.delBatch(ids);
        if (i == 0) {
            throw new BaseException(Constants.FAIL_DEL);
        }
        return i;
    }

    /**
     * 删除redis缓存的账户信息
     *
     * @param username
     */
    private void delRedisUser(String username) {
        if (redisUtil.hasKey(Constants.REDIS_USER_INFO)) {
            //清除账户信息
            redisUtil.del(Constants.REDIS_USER_INFO + username);
        }
        if (redisUtil.hasKey(Constants.REDIS_USER + username)) {
            //清除缓存security用户的数据
            redisUtil.del(Constants.REDIS_USER + username);
        }
    }


    /**
     * 查询所有(用户)
     *
     * @return 所有用户
     */
    @Override
    public List<SysUser> all() {
        return sysUserMapper.all();
    }


    /**
     * ID获取用户信息
     *
     * @param id 账户ID
     * @return 账户信息包含角色部门等
     */
    @Override
    public SysUserVo get(Long id) {
        if (StringUtils.isEmpty(id)) {
            throw new BaseException("数据ID为空");
        }
        SysUserVo userVo = sysUserMapper.getById(id);
        List<SysRole> sysRoles = sysRoleMapper.selectRoleByUserId(userVo.getId());
        List<SysPosition> sysPositions = sysPositionMapper.selectPositionByUserId(userVo.getId());
        userVo.setRoleList(sysRoles);
        userVo.setPositionList(sysPositions);
        return userVo;
    }


    /**
     * 激活禁用账户
     *
     * @param sysUser 用户实体
     * @return int
     */
    @Override
    public int active(SysUser sysUser) {
        SysUser user = sysUserMapper.selectByPrimaryKey(sysUser.getId());
        if (user == null) {
            throw new BaseException("账户不存在，可能已经被删除了");
        }
        if (user.getUsername().equals(servletUtil.getUserName()) && !sysUser.getActive()) {
            throw new BaseException("不能禁用自己");
        }
        int i = sysUserMapper.updateByPrimaryKeySelective(sysUser);
        if (i == 0) {
            throw new BaseException("更新账户信息失败");
        }
        //清除缓存用户的数据
        delRedisUser(user.getUsername());
        return i;
    }


    /**
     * 头像上传接口
     *
     * @param file 文件
     * @return 文件地址
     */
    @Override
    public String avatar(MultipartFile file) {
        String url = uploadUtil.upload(file, Constants.SERVER, avatarPath);
        HttpServletRequest request = ServletUtil.getHttpServletRequest();
        long userId = jwtTokenUtil.getUserId(request);
        SysUser sysUser = sysUserMapper.selectByPrimaryKey(userId);
        sysUser.setAvatar(url);
        if (sysUserMapper.updateByPrimaryKeySelective(sysUser) == 0) {
            throw new BaseException(Constants.FAIL_UPDATE);
        }
        // 修改成功,删除redis缓存的用户数据
        //缓存redis用户信息
        redisUtil.del(Constants.REDIS_USER_INFO + sysUser.getUsername());
        return url;
    }

    @Override
    public String avatarUpload(MultipartFile file) {
        String url = uploadUtil.upload(file, Constants.SERVER, avatarPath);
        return url;
    }


    /**
     * 注册
     *
     * @param regDto 注册账户实体
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void register(RegDto regDto) {
        //检测验证码
        checkCaptcha(regDto.getCaptcha(), regDto.getKey());
        if (!regDto.getPassword().equals(regDto.getConfirmPassword())) {
            throw new BaseException("两次输入密码不一致");
        }
        String encodePwd = passwordEncoder.encode(regDto.getPassword());
        SysUserVo userVo = new SysUserVo();
        userVo.setEmail(regDto.getEmail());
        checkUser(userVo);
        SysUser sysUser = new SysUser();
        BeanUtils.copyProperties(regDto, sysUser);
        sysUser.setActive(Constants.ON);
        sysUser.setAvatar(Constants.AVATAR);
        sysUser.setPassword(encodePwd);
        sysUser.setNickname(StringUtils.isEmpty(sysUser.getNickname()) ? sysUser.getUsername() : sysUser.getNickname());
        sysUser.setSex(Constants.SEX_SECRET);
        sysUser.setCreateTime(new Date());
        sysUser.setUpdateTime(new Date());
        //添加默认部门
        SysDept sysDept = sysDeptMapper.selectDeptByName(Constants.DEFAULT_DEPT);
        sysUser.setDeptId(sysDept.getId());
        if (sysUserMapper.insert(sysUser) == 0) {
            throw new BaseException("注册用户失败");
        }
        //添加默认角色
        SysRole sysRole = sysRoleMapper.selectRoleByName(Constants.DEFAULT_ROLE);
        List<Long> roleIds = new LinkedList<>();
        roleIds.add(sysRole.getId());
        sysRoleMapper.addRoleByUserId(sysUser.getId(), roleIds);
    }

    @Override
    public void logout(String token) {
        String usernameFromToken = StringUtils.isEmpty(token) ?
                jwtTokenUtil.getUsername(ServletUtil.getHttpServletRequest()) : jwtTokenUtil.getUsername(token);

        if (!StringUtils.isEmpty(usernameFromToken)) {
            //退出登录，删除缓存token
            redisUtil.del(Constants.REDIS_USER + usernameFromToken);
            //清除缓存的权限数据
            redisUtil.del(Constants.REDIS_USER_AUTH + usernameFromToken);
            //redis缓存用户token
            redisUtil.del(Constants.USER_TOKEN + usernameFromToken);
            //缓存redis用户信息
            redisUtil.del(Constants.REDIS_USER_INFO + usernameFromToken);
            //缓存redis在线用户
            redisUtil.del(Constants.ONLINE_KEY + usernameFromToken);
        } else {
            throw new BaseException("未登录，退出无效");
        }
        //获取账户的信息
//        SecurityContextHolder.getContext().getAuthentication();
    }

    @Override
    public List<SysUser> emailAll() {
        return sysUserMapper.emailAll();
    }

    @Override
    public void resetPwd(UserPwd userPwd) {
        if (userPwd == null || userPwd.getUserId() == null || StringUtils.isEmpty(userPwd.getNewPassword())) {
            throw new BaseException("用户ID和新密码不能为空,缺少参数");
        }
        SysUser sysUser = new SysUser();
        sysUser.setId(userPwd.getUserId());
        // 设置密码加密
        sysUser.setPassword(passwordEncoder.encode(userPwd.getNewPassword()));
//        sysUser.setPassword(userPwd.getNewPassword());
        if (sysUserMapper.updateByPrimaryKeySelective(sysUser) == 0) {
            throw new BaseException("重置失败");
        }
    }

    @Override
    public void authRole(Long userId, List<Long> roleIds) {
        if (userId == null) {
            throw new BaseException("用户ID未选择");
        }
        if (roleIds == null || roleIds.size() == 0) {
            throw new BaseException("角色ID未选择");
        }
        try {
            this.delRoleByUserId(userId);
            this.addRoleByUserId(userId, roleIds);
        } catch (Exception e) {
            throw new BaseException("授权角色失败");
        }
    }

    @Override
    public void updateUserPwd(UserPwd userPwd) {
        if (userPwd == null || StringUtils.isEmpty(userPwd.getOldPassword()) || StringUtils.isEmpty(userPwd.getNewPassword())) {
            throw new BaseException("旧密码和新密码不能为空,缺少参数");
        }
        HttpServletRequest request = ServletUtil.getHttpServletRequest();
        String username = jwtTokenUtil.getUsername(request);
        SysUser sysUser = sysUserMapper.selectByUsername(username);
//        if (!sysUser.getPassword().equals(userPwd.getOldPassword())) {
        //匹配原密码
        if (passwordEncoder.matches(userPwd.getOldPassword(), sysUser.getPassword())) {
            throw new BaseException("原密码不正确");
        }
        sysUser.setPassword(userPwd.getNewPassword());
        if (sysUserMapper.updateByPrimaryKeySelective(sysUser) == 0) {
            throw new BaseException("修改密码失败");
        }
    }

    @Override
    public void updateUserProfile(String avatar, String nickname, String email, String phone, Integer sex) {
        if (StringUtils.isEmpty(nickname)) {
            throw new BaseException("昵称不能为空");
        }
        if (StringUtils.isEmpty(email)) {
            throw new BaseException("邮箱不能为空");
        }
        if (StringUtils.isEmpty(phone)) {
            throw new BaseException("手机不能为空");
        }
        if (StringUtils.isEmpty(sex)) {
            throw new BaseException("性别不能为空");
        }
        HttpServletRequest request = ServletUtil.getHttpServletRequest();
        String username = jwtTokenUtil.getUsername(request);
        SysUser sysUser = sysUserMapper.selectByUsername(username);
        sysUser.setNickname(nickname);
        sysUser.setSex(sex.shortValue());
        sysUser.setPhone(phone);
        sysUser.setEmail(email);
        if (!StringUtils.isEmpty(avatar)) {
            sysUser.setAvatar(avatar);
        }
        if (sysUserMapper.updateByPrimaryKeySelective(sysUser) == 0) {
            throw new BaseException("修改失败");
        }
    }

    @Override
    public void online() {
        //获取用户token
        HttpServletRequest request = ServletUtil.getHttpServletRequest();
        String userToken = jwtTokenUtil.getToken(request);
        SysUserVo userVo = info(userToken);
        OnlineVo onlineVo = new OnlineVo();
        BeanUtils.copyProperties(userVo, onlineVo);

        //保存在线信息
        onlineVo.setToken(userToken);
        onlineVo.setBrowser(ServletUtil.getBrowser(ServletUtil.getHttpServletRequest()));
        onlineVo.setOs(ServletUtil.getOs(ServletUtil.getHttpServletRequest()));
        onlineVo.setRequestIp(ServletUtil.getIpAddr(ServletUtil.getHttpServletRequest()));
        onlineVo.setRequestAddress(ServletUtil.getAddress(onlineVo.getRequestIp()));
        onlineVo.setRestUrl(ServletUtil.getHttpServletRequest().getRequestURL().toString());
        onlineVo.setOnlineStatus("在线");
        //混淆用户token信息，加密工具加密
        String decodeBase64 = Base64.encodeBase64URLSafeString(onlineVo.getToken().getBytes(StandardCharsets.UTF_8));
//        String decodeBase64 = new String(decode);
        onlineVo.setToken(decodeBase64);
        try {
            redisUtil.set(Constants.ONLINE_KEY + decodeBase64, onlineVo, 60 * 11L);
        } catch (Exception e) {
            throw new BaseException("同步失败");
        }
    }

    @Override
    public Map<String, Object> onlineList(String userName, String ipaddr) {
        //查询全部在线用户
        Set<String> keys = redisUtil.getKeys(Constants.ONLINE_KEY);
        List<OnlineVo> onlineVos = new LinkedList<>();
        for (String key : keys) {
            OnlineVo onlineVo = (OnlineVo) redisUtil.get(key);
            //没有模糊匹配参数
            if (StringUtils.isEmpty(userName) && StringUtils.isEmpty(ipaddr)) {
                onlineVos.add(onlineVo);
                //模糊匹配用户名
            } else if (!StringUtils.isEmpty(userName)) {
                if (onlineVo.getUsername().contains(userName)) {
                    onlineVos.add(onlineVo);
                }
                //模糊匹配ip地址
            } else if (!StringUtils.isEmpty(ipaddr)) {
                if (onlineVo.getRequestIp().contains(ipaddr)) {
                    onlineVos.add(onlineVo);
                }
            }
        }
        //返回数据
        Map<String, Object> map = new HashMap<>();
        map.put("rows", onlineVos);
        map.put("total", onlineVos.size());
        return map;
    }

    @Override
    public void forceLogout(String token) {
        if (StringUtils.isEmpty(token)) {
            throw new BaseException("获取token为空");
        }
        byte[] decode = Base64.decodeBase64(token.getBytes());
        String decodeBase64 = new String(decode);
        String username = jwtTokenUtil.getUsername(decodeBase64);
        //删除token信息,是否为多端登录
        if (isManyLogin()) {
            redisUtil.del(Constants.USER_TOKEN + username + "_" + decodeBase64);
        } else {
            redisUtil.del(Constants.USER_TOKEN + username);
        }
        //删除在线用户信息
        redisUtil.del(Constants.ONLINE_KEY + token);
    }

    @Override
    public void export() throws IOException {
        HttpServletResponse response = ServletUtil.getHttpServletResponse();
        // 查询数据
        List<SysUserVo> userList = sysUserMapper.export();
        InputStream is = ExcelUtil.inputStreamExcel(userList, "用户信息", "用户信息sheet",
                SysUserVo.class, "用户信息表", response);
        ExcelUtil.downloadExcel("用户列表.xlsx", is, response);

    }

    @Override
    public void forgetVerify(String email) {
        if (!StringUtil.isEmail(email)) {
            throw new BaseException("邮箱格式有错误");
        }
        SysUser sysUser = sysUserMapper.selectByEmail(email);
        if (sysUser == null) {
            throw new BaseException("邮箱不存在");
        }
        String randomNnm = StringUtil.randomNnm(4);
        redisUtil.set(Constants.FORGET_KEY + sysUser.getEmail(), randomNnm, 300L);
        StringBuffer sb = new StringBuffer();
        sb.append("您正在找回账户：").append(sysUser.getUsername())
                .append("(").append(sysUser.getEmail()).append(")，随机验证码为：")
                .append(randomNnm).append(",有效期5分钟！");
        mailService.sendSimpleTextMailActual("忘记密码提醒", sb.toString(), new String[]{sysUser.getEmail()},
                null, null, null);
    }

    @Override
    public void reset(ResetUserDto resetUserDto) {
        if (!StringUtil.isEmail(resetUserDto.getEmail())) {
            throw new BaseException("邮箱格式有错误");
        }
        SysUser sysUser = sysUserMapper.selectByEmail(resetUserDto.getEmail());
        if (sysUser == null) {
            throw new BaseException("邮箱不存在");
        }
        String captcha = (String) redisUtil.get(Constants.FORGET_KEY+ resetUserDto.getEmail());
        if (captcha==null){
            throw new BaseException("验证码已失效，请重新获取");
        }
        if (!captcha.equals(resetUserDto.getCaptcha())){
            throw new BaseException("验证码不正确");
        }
        if (!resetUserDto.getPassword().equals(resetUserDto.getConfirmPassword())){
            throw new BaseException("两次输入密码不一致");
        }
        //md5加密新密码
        String newPwd = DigestUtils.md5DigestAsHex(resetUserDto.getPassword().getBytes());
        sysUser.setPassword(passwordEncoder.encode(newPwd));
        sysUser.setUpdateTime(new Date());
        if (sysUserMapper.updateByPrimaryKeySelective(sysUser)==0){
            throw new BaseException("重置密码失败");
        }
    }

    /**
     * 文件下载
     *
     * @param destination 下载路径
     * @param input
     * @throws IOException
     */
    public void writeToLocal(String destination, InputStream input)
            throws IOException {
        int index;
        byte[] bytes = new byte[1024];
        FileOutputStream downloadFile = new FileOutputStream(destination);
        while ((index = input.read(bytes)) != -1) {
            downloadFile.write(bytes, 0, index);
            downloadFile.flush();
        }
        input.close();
        downloadFile.close();

    }

}